//*********************************************
//  This is Geant4 Template
//                                  author:Qian
//

#include "Verbose.hh"
#include "MyDetectorConstruction.hh"
#include "MySDDetector.hh"
#include "G4SDManager.hh"
#include "G4GDMLParser.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4PhysicalVolumeStore.hh"

#include "G4GlobalMagFieldMessenger.hh"
#include "G4AutoDelete.hh"

#include "MyDetectorReader.hh"
#include "MyDetectorMessenger.hh"
#include "MyDetectorSettings.hh"
#include "MyMagneticField.hh"

#include "G4Colour.hh"
#include "G4VisAttributes.hh"
#include "G4NistManager.hh"
#include "G4PVPlacement.hh"
#include "G4TriangularFacet.hh"

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

MyDetectorConstruction::MyDetectorConstruction()
    : G4VUserDetectorConstruction(),
      fReader(0), fWriter(0), fParser(0), fSettings(0)
{
    //if (verbose)
    G4cout << "====>MyDetectorConstruction::MyDetectorConstruction()" << G4endl;

    fReadFile = "./gdml/main.gdml";
    fWriteFile = "./gdml/main_writer.gdml";
    fWritingFlag = 0;

    fDetectorMessenger = new MyDetectorMessenger(this);

    fReader = new MyDetectorReader;
    fParser = new G4GDMLParser(fReader, fWriter);

    fSettings = new MyDetectorSettings;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

MyDetectorConstruction::~MyDetectorConstruction()
{
    delete fDetectorMessenger;
    delete fReader;
    delete fParser;
    delete fSettings;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

G4VPhysicalVolume *MyDetectorConstruction::Construct()
{
    //if (verbose)
    G4cout << "====>MyDetectorConstruction::Construct()" << G4endl;

    //1. using gdml
    //fParser->Read(fReadFile, false);
    //fSettings->ApplyAuxValue(fParser);

    ////Get volume by it's name...
    ////G4LogicalVolumeStore::GetInstance()->GetVolume("Volume Name");

    ////#Verb 2. 打开Material的输出
    ////G4cout << *(G4Material::GetMaterialTable()) << G4endl;
    //return fParser->GetWorldVolume();

    //2. using LogicalVolume
    //
    // World
    //
    G4bool checkOverlaps = true;
    G4double world_sizeXY = 10000000;
    G4double world_sizeZ = 100000;
    G4NistManager *nist = G4NistManager::Instance();
    G4Material *world_mat = nist->FindOrBuildMaterial("G4_AIR");
    G4Material *env_mat = nist->FindOrBuildMaterial("G4_Water");
    G4Box *solidWorld = new G4Box("World", 0.5 * world_sizeXY, 0.5 * world_sizeXY, 0.5 * world_sizeZ);
    G4LogicalVolume *logicWorld = new G4LogicalVolume(solidWorld, world_mat, "World");
    G4VPhysicalVolume *physWorld = new G4PVPlacement(0, G4ThreeVector(), logicWorld, "World", 0, false, 0, checkOverlaps);

    auto visAttributes = new G4VisAttributes(G4Colour(1.0, 1.0, 1.0));
    visAttributes->SetVisibility(false);
    logicWorld->SetVisAttributes(visAttributes);

    //2.1 Tessellate Facet
    G4TessellatedSolid *solidTarget = new G4TessellatedSolid("Solid_name");

    /* //2.1.1 test facet
    G4double targetSize = 10;
    G4ThreeVector a1(0, 0, targetSize);
    G4ThreeVector a2(-targetSize, 0, 0);
    G4ThreeVector a3(targetSize, 0, 0);
    G4ThreeVector a4(0, targetSize, 0);

    G4TriangularFacet *facet1 = new G4TriangularFacet(a1, a2, a3, ABSOLUTE);
    G4TriangularFacet *facet2 = new G4TriangularFacet(a1, a2, a4, ABSOLUTE);
    G4TriangularFacet *facet3 = new G4TriangularFacet(a1, a4, a3, ABSOLUTE);
    G4TriangularFacet *facet4 = new G4TriangularFacet(a2, a4, a3, ABSOLUTE);

    // Now add the facets to the solid
    //
    solidTarget->AddFacet((G4VFacet *)facet1);
    solidTarget->AddFacet((G4VFacet *)facet2);
    solidTarget->AddFacet((G4VFacet *)facet3);
    solidTarget->AddFacet((G4VFacet *)facet4);

    solidTarget->SetSolidClosed(true);
    */

    //2.1.2 read from filee
    std::ifstream fd("../ghost_mansion_reduced.ply");
    std::vector<G4ThreeVector> plist;
    double x0, y0, z0;
    double xt, yt, zt;
    double xmin, xmax, ymin, ymax, zmin, zmax;
    int i0, i1, i2, i3;

    fd >> x0 >> y0 >> z0;
    plist.push_back(G4ThreeVector(0, 0, 0));

    xmin = ymin = zmin = +999999;
    xmax = ymax = zmax = -999999;
    for (int i = 1; i < 83770; i++)
    {
        fd >> xt >> yt >> zt;
        xt -= x0;
        yt -= y0;
        zt -= z0;

        xmin = (xt<xmin)?xt:xmin;
        ymin = (yt<ymin)?yt:ymin;
        zmin = (zt<zmin)?zt:zmin;

        xmax = (xt>xmax)?xt:xmax;
        ymax = (yt>ymax)?yt:ymax;
        zmax = (zt>zmax)?zt:zmax;

        plist.push_back(G4ThreeVector(xt, yt, zt));
    }
    G4cout<<"x=("<<xmin<<", "<<xmax<<"), y=("<<ymin<<", "<<ymax<<"), z=("<<zmin<<", "<<zmax<<")"<<G4endl;

    for (int i = 83770; i < 250226; i++)
    {
        fd >> i0 >> i1 >> i2 >> i3;
        G4TriangularFacet *facet = new G4TriangularFacet(plist[i1], plist[i2], plist[i3], ABSOLUTE);
        solidTarget->AddFacet((G4VFacet *)facet);
    }

    solidTarget->SetSolidClosed(true);

    /*
    //2.1.3 test xtru
    std::vector<G4TwoVector> polygon;
    polygon.push_back(G4TwoVector(-3, -3));
    polygon.push_back(G4TwoVector(3, -3));
    polygon.push_back(G4TwoVector(5, 10));
    polygon.push_back(G4TwoVector(3, 3));
    polygon.push_back(G4TwoVector(-3, 3));
    G4ExtrudedSolid *solidTarget = new G4ExtrudedSolid("Solid_name", polygon, 2, G4TwoVector(0, 0), 3., G4TwoVector(0, 0), 1.);
    */
    //2.x common part
    G4LogicalVolume *logicEnv = new G4LogicalVolume(solidTarget, env_mat, "Envelope");
    new G4PVPlacement(0, G4ThreeVector(), logicEnv, "Envelope", logicWorld, false, 0, checkOverlaps);

    G4Box *TombBox = new G4Box("TombBox", 6.8, 4.25, 5.);
    G4LogicalVolume *logicTomb = new G4LogicalVolume(TombBox, world_mat, "Tomb");
    new G4PVPlacement(0, G4ThreeVector(0, 0, 10), logicTomb, "Tomb", logicEnv, false, 0, checkOverlaps);


    /*
    //3. boxes
    int length = sizeof(unsigned short);
    unsigned short memblock;
    std::fstream InDat;
    InDat.open("../ghost_mansion_data3d_w1000_h790_f149_u16.raw", std::ios::in | std::ios::binary);

    G4Box *MudBox = new G4Box("mudBox", 1., 1., 1.);
    G4LogicalVolume *logicMud = new G4LogicalVolume(MudBox, env_mat, "Envelope");

    for (int x = 0; x < 1000; x++)
        for (int y = 0; y < 790; y++)
            for (int z = 0; z < 1; z++)
            {
                InDat.read((char *)(&memblock), length);
                if (memblock == 0)
                    continue;

                G4cout<<"here: x="<<x<<", y="<<y<<", z="<<z<<G4endl;
                new G4PVPlacement(0, G4ThreeVector(x, y, z), logicMud, "Envelope", logicWorld, false, 0, false);
            }
    */

    return physWorld;
}

void MyDetectorConstruction::ConstructSDandField()
{
    if (verbose)
        G4cout << "====>MyDetectorConstruction::ConstructSDandField()" << G4endl;

    //------------------------------------------------
    // 定义SD
    //------------------------------------------------

    /*
    G4SDManager *SDman = G4SDManager::GetSDMpointer();
    MySDRich *aRICH = new MySDRich("RICH");
    SDman->AddNewDetector(aRICH);

    //------------------------------------------------
    // Sensitive detectors GDML 定义
    //------------------------------------------------
    //  在RICH.gdml里，定义如下关键词
    //        <volume name="ReadoutBoxVol">                     定义了读出体
    //      	    <materialref ref="G4_Cu" />                 定义了读出体的材质
    //      		<solidref ref="ReadoutBox" />               定义了读出体的结构尺寸
    //      		<auxiliary auxtype="SensDet" auxvalue="RICH"/>    SensDet表示这是个读出体，其值为RICH，这个名字必须和上面的SDMananger那里对应上
    //	      </volume>
    //------------------------------------------------
    // 注意，这里RICH的logicVolume只有一个，但是放置了好几次，因此会有多个PhysicsVolume
    //
    const G4GDMLAuxMapType *auxmap = fParser->GetAuxMap();
    for (G4GDMLAuxMapType::const_iterator iter = auxmap->begin(); iter != auxmap->end(); iter++)
    {
        for (G4GDMLAuxListType::const_iterator vit = (*iter).second.begin(); vit != (*iter).second.end(); vit++)
        {
            if ((*vit).type == "SensDet")
            {
                G4cout << "Attaching sensitive detector " << (*vit).value
                       << " to volume " << ((*iter).first)->GetName()
                       << " " << ((*iter).first) << G4endl << G4endl;

                G4VSensitiveDetector *mydet = SDman->FindSensitiveDetector((*vit).value);
                if (mydet)
                {
                    G4LogicalVolume *myvol = (*iter).first;
                    myvol->SetSensitiveDetector(mydet);
                }
            }
        }
    }
    */

    //------------------------------------------------
    // Fields

    //1. 全局磁场 ------------------------------------------------
    //#MagField 1. 构造全局磁场
    // 若在XML中没有定义磁场，则默认采用全局磁场（B=0），可通过mac文件用以下命令控制全局磁场
    //       /globalField/setValue 1.0 0 0 tesla

    if (fSettings->GetUserMagFieldFlag() == 0)
    {
        G4ThreeVector fieldValue;
        fMagFieldMessenger = new G4GlobalMagFieldMessenger(fieldValue);
        fMagFieldMessenger->SetVerboseLevel(1);

        // Register the field messenger for deleting
        G4AutoDelete::Register(fMagFieldMessenger);
    }

    //2. 局部磁场 ------------------------------------------------
    //#MagField 2. 构造局部磁场
    // 这里磁场是定义在 MagTubeVol 所包含的逻辑体内部，注意这里的名字和 XML 里要相互对应

    if (fSettings->GetUserMagFieldFlag() != 0)
    {
        fMagneticField = new MyMagneticField();
        fFieldMgr = new G4FieldManager();
        fFieldMgr->SetDetectorField(fMagneticField);
        fFieldMgr->CreateChordFinder(fMagneticField);
        G4bool forceToAllDaughters = true;
        auto *fMagneticLogical = G4LogicalVolumeStore::GetInstance()->GetVolume("MagTubeVol");
        fMagneticLogical->SetFieldManager(fFieldMgr, forceToAllDaughters);

        // Register the field and its manager for deleting
        G4AutoDelete::Register(fMagneticField);
        G4AutoDelete::Register(fFieldMgr);
    }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void MyDetectorConstruction::SetReadFile(const G4String &fname)
{
    if (verbose)
        G4cout << "====>MyDetectorConstruction::SetReadFile()" << G4endl;

    fReadFile = fname;
    fWritingFlag = 0;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void MyDetectorConstruction::SetWriteFile(const G4String &fname)
{
    if (verbose)
        G4cout << "====>MyDetectorConstruction::SetWriteFile()" << G4endl;

    fWriteFile = fname;
    fWritingFlag = 1;
}
